package bkt.framework.tool;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import bkt.framework.FrameworkConstants;
import bkt.framework.db.TableImportUnit;
import net.sqlcipher.database.SQLiteDatabase;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.content.ContextWrapper;
import android.database.Cursor;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.util.Log;


public class FrameworkDatabaseUtil {

	private static volatile boolean isLibraryLoaded = false;
	private List<TableImportUnit> dbData = null;
	
	public void loadLibraries(Context context) {
		if(!isLibraryLoaded){
			SQLiteDatabase.loadLibs(context);
			FrameworkDatabaseUtil.isLibraryLoaded = true;
		}
	}

	public synchronized boolean isDBExist(String file_path, String dbKey) {
		SQLiteDatabase db = null;
		boolean retVal= false;
		try {
			db = SQLiteDatabase.openDatabase(file_path, dbKey, null, SQLiteDatabase.OPEN_READONLY);
		} catch (Exception e) {
			Log.w(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL,"DB notExist",e);
		}finally{
			dbKey = null;
		}
		if(db != null){
			retVal = true;
			if(db.isOpen()){
				db.close();
			}
		}
		return retVal;
	}

	public void copyDatabaseFile(Context context, String dbName, String dbPath, String dbAbsolutePath) {
		InputStream myInput = null;
		BufferedOutputStream myOutPut = null;
		
		try {
			myInput = context.getAssets().open(dbName);
			
			File fileDir =  new File(dbPath);
			fileDir.mkdir();
			
			File file = new File(dbAbsolutePath);
			if(!file.createNewFile()){
				Log.i(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL, "File Exist");
			}
			myOutPut =  new BufferedOutputStream(new FileOutputStream(file),1024);
			byte[] buffer =  new byte[1024];
			int length;
			while ((length=myInput.read(buffer))>0) {
				myOutPut.write(buffer,0,length);
			}
		}
		catch(FileNotFoundException e){
			Log.e(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL, "File Not Found dbname : '"+ dbName+"'",e);
		}
		catch(IOException e){
			Log.e(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL, "IO Error dbPath : '"+ dbPath+"'",e);
		}
		catch (Exception e) {
			Log.e(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL, "DB CopyError",e);
		}finally{
			try {
				myInput.close();
				myOutPut.flush();
				myOutPut.close();
			} catch (Exception ex) {
				Log.e(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL, "Steram Close Error",ex);
			}
		}
		
	}

	public void loadDecryptedDB(Context context, String dbNameInAssetFolder,String masterTableName, int dbVersion, String[] excludedTables) {
		android.database.sqlite.SQLiteDatabase sqLiteDatabase =  null;
		android.database.Cursor tableDataReaderCursor = null;
		SQLiteOpenHelper sqLiteOpenHelper = new  android.database.sqlite.SQLiteOpenHelper(context,dbNameInAssetFolder,null,dbVersion) {
			@Override
			public void onUpgrade(android.database.sqlite.SQLiteDatabase db,int oldVersion, int newVersion) {
			}
			
			@Override
			public void onCreate(android.database.sqlite.SQLiteDatabase db) {
			}
		};  
		
		
		try {
			sqLiteDatabase = sqLiteOpenHelper.getReadableDatabase();
			tableDataReaderCursor =  sqLiteDatabase.query(masterTableName,null,null,null,null,null,null);

			if(tableDataReaderCursor != null && tableDataReaderCursor.getCount()>0){

				String[] masterTableColumnNameArr = tableDataReaderCursor.getColumnNames();
				int masterTableTableNameColumnIndex = -1, masterTableSqlColumnIndex = -1;

				for(int i = 0; i<masterTableColumnNameArr.length;i++){
					if(!TextUtils.isEmpty(masterTableColumnNameArr[i])){
						if(masterTableColumnNameArr[i].equalsIgnoreCase(FrameworkConstants.MASTER_TABLE_NAME_COLUMN)){
							masterTableTableNameColumnIndex = i;
						}else if (masterTableColumnNameArr[i].equalsIgnoreCase(FrameworkConstants.MASTER_TABLE_SQL_COLUMN)){
							masterTableSqlColumnIndex = i;
						}
						if(masterTableSqlColumnIndex > 0 && masterTableTableNameColumnIndex >0){
							break;
						}

					}
				}

				if(masterTableSqlColumnIndex > 0 && masterTableTableNameColumnIndex >0){
					boolean isExcludedTableExist = excludedTables != null && excludedTables.length>0? true : false;
					if(isExcludedTableExist){
						Arrays.sort(excludedTables);
					}
					
					if(dbData == null){
						dbData =  new ArrayList<TableImportUnit>();
					}else{
						dbData.clear();
					}
					
					
					tableDataReaderCursor.moveToFirst();
					
					String currTableName = null,currTableCreateSQL=null;
					android.database.Cursor curTableCursor = null;
					TableImportUnit curTableImportUnit = null;
					
					while (!tableDataReaderCursor.isAfterLast()) {
						currTableName = null;
						currTableCreateSQL=null;
						curTableCursor = null;
						curTableImportUnit = null;
						
						currTableName = tableDataReaderCursor.getString(masterTableTableNameColumnIndex);
						currTableCreateSQL = tableDataReaderCursor.getString(masterTableSqlColumnIndex);
						
						if(!TextUtils.isEmpty(currTableName) && !TextUtils.isEmpty(currTableCreateSQL)){
							if(isExcludedTableExist){
								if(Arrays.binarySearch(excludedTables, currTableName)>-1){
									tableDataReaderCursor.moveToNext();
									continue;
								}
							}
							curTableImportUnit = readTableData(sqLiteDatabase,null,currTableName,currTableCreateSQL,curTableCursor);
							if(curTableImportUnit!= null){
								dbData.add(curTableImportUnit);
							}
						}
						tableDataReaderCursor.moveToNext();
					}
					
				}//end of masterTableSqlColumnIndex > 0 && masterTableTableNameColumnIndex >0
			}//end of tableDataReaderCursor != null && tableDataReaderCursor.getCount()>0
			
			
		} catch (Exception e) {
			Log.e(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL,"Can Not Load Decrypted Data",e);
		}finally{
			try {
				if(tableDataReaderCursor != null && ! tableDataReaderCursor.isClosed()){
					tableDataReaderCursor.close();
					tableDataReaderCursor = null;
				}
				if(sqLiteDatabase != null ){
					if(sqLiteDatabase.isOpen()){
						sqLiteDatabase.close();
					}
					sqLiteDatabase= null;
				}
				if(sqLiteOpenHelper != null){
					sqLiteOpenHelper = null;
				}
				
			} catch (Exception e) {
				Log.e(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL,"Stream closing get Error",e);
			}
		}
		
		
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private TableImportUnit readTableData(android.database.sqlite.SQLiteDatabase sqLiteDatabase, SQLiteDatabase encrDB, String currTableName, String currTableCreateSQL, android.database.Cursor curTableCursor) {
		TableImportUnit curTableImportUnit =  new TableImportUnit();
		curTableImportUnit.setCreateQuery(currTableCreateSQL);
		curTableImportUnit.setTableName(currTableName);

		try {
			curTableCursor = sqLiteDatabase != null ? 
					sqLiteDatabase.query(currTableName,null,null,null,null,null,null):
					encrDB.query(currTableName,null,null,null,null,null,null);
			
			if(curTableCursor != null && curTableCursor.getCount()>0){
				
				HashMap<String, Object> curRowData = null;
				ArrayList curTableDataList = curTableImportUnit.getTableData();
				curTableCursor.moveToFirst();
				while (!curTableCursor.isAfterLast()) {
					curRowData = new HashMap<String,Object>();
					for(int i = 0; i< curTableCursor.getColumnCount();i++){
						String curTableColumnName = curTableCursor.getColumnName(i);
						try {
							Object columnData = getColumnDataFromCursor(curTableCursor,i);
							curRowData.put(curTableColumnName, columnData);
						} catch (Exception e) {
							Log.e(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL, "Colum Data Error",e);
							curRowData.clear();
							break;
						}
					}
					if(curRowData.size()>0 && !isAllHashMapDataNull(curRowData)){
						curTableDataList.add(curRowData);
					}
					curTableCursor.moveToNext();
				}
				
				
			}
		} catch (Exception e) {
			Log.e(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL,"Table Read Error",e);
		}finally{
			if(curTableCursor != null && !curTableCursor.isClosed()){
				curTableCursor.close();
				curTableCursor = null;
			}
		}
		
		return curTableImportUnit;
	}

	@SuppressLint("NewApi")
	private Object getColumnDataFromCursor(Cursor curTableCursor, int i) {
		Object retVal = null;
		
		switch (curTableCursor.getType(i)) {
		case Cursor.FIELD_TYPE_BLOB:
			retVal = curTableCursor.getBlob(i);
			break;
		case Cursor.FIELD_TYPE_FLOAT:
			retVal = curTableCursor.getFloat(i);
			break;
		case Cursor.FIELD_TYPE_INTEGER:
			retVal = curTableCursor.getInt(i);
			break;
		case Cursor.FIELD_TYPE_STRING:
			retVal = curTableCursor.getString(i);
		default:
			break;
		}
		
		return retVal;
	}
	
	@SuppressWarnings("rawtypes")
	private boolean isAllHashMapDataNull(HashMap<String, Object> curRowData) {
		Iterator iterator = curRowData.entrySet().iterator();
		while (iterator.hasNext()) {
			Map.Entry  entry= (Map.Entry) iterator.next();
			if(entry.getValue() != null){
				return false;
			}
		}
		return true;
	}

	public SQLiteDatabase encryptDatabase(Context context, String dbName, String dbKey) {
		SQLiteDatabase database = null;
		ContextWrapper contextWrapper = new ContextWrapper(context);
		
		File databaseFile = contextWrapper.getDatabasePath(dbName);
		databaseFile.mkdir();
		
		if(databaseFile.delete()){
			Log.w(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL, "Decrypted File Can Not Delete");
		}
		
		try {
			
			database= SQLiteDatabase.openOrCreateDatabase(databaseFile,dbKey,null);
			
		} catch (Exception e) {
			Log.e(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL, "Caanot Create Encrypted DB ",e);
		}finally{
			contextWrapper = null;
			databaseFile = null;
		}
		return database;
	}

	@SuppressWarnings("rawtypes")
	public void importData(SQLiteDatabase encrDB, String dbKey) {
		if(encrDB!= null && dbData != null && dbData.size()>0){
			for (int i = 0; i < dbData.size(); i++) {
				TableImportUnit curUnit = dbData.get(i);
				try {
					encrDB.execSQL(curUnit.getCreateQuery());
				} catch (Exception e) {
					Log.e(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL, "Create ENCR DB Table has Error",e);
				}
				ArrayList curTableData = curUnit.getTableData();
				
				if(curTableData!= null && curTableData.size()>0){
				
					for (int j = 0; j < curTableData.size(); j++) {
						@SuppressWarnings("unchecked")
						HashMap<String, Object> tableRowData = (HashMap<String, Object>) curTableData.get(j);
						if(tableRowData != null && tableRowData.size()>0){
							try {
								ContentValues curValue = populateTableRowContentData(tableRowData);
								encrDB.insert(curUnit.getTableName(), dbKey, curValue);
								curValue = null;
							} catch (Exception e) {
								Log.e(FrameworkConstants.LOGTAG_FRAMEWORK_DATABASE_UTIL, "Insert Table Get Error",e);
							}
						}
					}
					
				}
				
			}
		}
	}

	@SuppressWarnings("rawtypes")
	private ContentValues populateTableRowContentData(HashMap<String, Object> tableRowData) {
		ContentValues values =  new ContentValues();
		Iterator iterator = tableRowData.entrySet().iterator();
		while (iterator.hasNext()) {
			Map.Entry entry = (Map.Entry)iterator.next();
			values.put(entry.getKey().toString(), entry.getValue() != null ? entry.getValue().toString():null);
		}
		return values;
	}

}
